package co.infinum.goldfinger;

import java.util.concurrent.atomic.AtomicReference;

import co.infinum.goldfinger.crypto.ResourceTable;
import co.infinum.goldfinger.crypto.MarshmallowReprintModule;
import co.infinum.goldfinger.crypto.ReprintModule;
import ohos.aafwk.ability.Ability;
import ohos.biometrics.authentication.BiometricAuthentication;

/**
 * Goldfinger implementation for  Marshmallow and newer.
 */
class GoldfingerImpl implements Goldfinger {

    public static final Logger NULL_LOGGER = new Logger() {
        public void log(String message) {
        }

        public void logException(Throwable throwable, String message) {
        }
    };

    private AtomicReference<BiometricAuthentication> cancellationSignal = new AtomicReference<>();
    private ReprintModule module;
    private Ability context;
    private BiometricAuthentication.SecureLevel level;
    private BiometricAuthentication.AuthType type;

    public GoldfingerImpl(Ability context, BiometricAuthentication.AuthType type, BiometricAuthentication.SecureLevel level, Goldfinger.Logger logger) {
        this.context = context;
        this.level = level;
        this.type = type;
        if (logger == null) {
            logger = NULL_LOGGER;
        }
        MarshmallowReprintModule marshmallowModule = new MarshmallowReprintModule(context, logger);
        marshmallowModule.init(type, level);
        registerModule(marshmallowModule);
    }

    @Override
    public void registerModule(ReprintModule module) {
        if (module == null || this.module != null && module.tag() == this.module.tag()) {
            return;
        }
        this.module = module;
    }

    @Override
    public int isHardwarePresent() {
        try {
            return module.isHardwarePresent();
        } catch (Exception e) {
            e.printStackTrace();
            return FaceAuthResult.ERROR_UNKNOWN;
        }
    }


    @Override
    public boolean canAuthenticate() {
        return module.isHardwarePresent() == BiometricAuthentication.BA_SUCCESS;
    }

    /**
     * Start an authentication request.
     *
     * @param callback         The listener to be notified.
     * @param restartPredicate The predicate that determines whether to restart or not.
     */
    @Override
    public void authenticate(RestartPredicate restartPredicate, Callback callback) {
        if (preconditionsInvalid(null, null, callback)) {
            return;
        }
        try {
            BiometricAuthentication mBiometricAuthentication = BiometricAuthentication.getInstance(context);
            cancellationSignal.set(mBiometricAuthentication);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        // 异常情况下考虑 null
        module.authenticate(cancellationSignal.get(), callback, restartPredicate);
    }

    /**
     * Returns BA_SUCCESS if the authentication is canceled; returns BA_FAILED otherwise.
     * 取消认证
     *
     * @return int
     */
    @Override
    public int cancelAuthentication() {
        int result = -1;
        final BiometricAuthentication signal = cancellationSignal.getAndSet(null);
        if (signal != null) {
            result = signal.cancelAuthenticationAction();
        }
        return result;
    }

    @Override
    public void encrypt(RestartPredicate restartPredicate, String key, String value, Callback callback) {
        if (preconditionsInvalid(null, null, callback)) {
            return;
        }
        try {
            BiometricAuthentication mBiometricAuthentication = BiometricAuthentication.getInstance(context);
            cancellationSignal.set(mBiometricAuthentication);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        // 异常情况下考虑 null
        module.authenticate(cancellationSignal.get(), callback, restartPredicate, key, value);
    }

    private boolean preconditionsInvalid(String key, String value, Callback callback) {
        if (module == null) {
            new MissingHardwareException(FaceAuthResult.getString(context, ResourceTable.String_NO_HARDWARE));
            return true;
        }
        if (!canAuthenticate()) {
            String msg = "";

            switch (isHardwarePresent()) {
                case 1:
                    msg = FaceAuthResult.getString(context, ResourceTable.String_BA_CHECK_AUTH_TYPE_NOT_SUPPORT);
                    break;
                case 2:
                    msg = FaceAuthResult.getString(context, ResourceTable.String_BA_CHECK_SECURE_LEVEL_NOT_SUPPORT);
                    break;
                case 3:
                    msg = FaceAuthResult.getString(context, ResourceTable.String_BA_CHECK_DISTRIBUTED_AUTH_NOT_SUPPORT);
                    break;
                case 4:
                    msg = FaceAuthResult.getString(context, ResourceTable.String_BA_CHECK_NOT_ENROLLED);
                    break;
                case 5:
                    msg = FaceAuthResult.getString(context, ResourceTable.String_BA_CHECK_UNAVAILABLE);
                    break;
                default:
                    msg = FaceAuthResult.getString(context, ResourceTable.String_ERROR_UNKNOWN);
                    break;
            }
            callback.onError(new MissingHardwareException(msg));
            return true;
        }
        if (type == null) {
            callback.onError(new MissingHardwareException("未配置认证类型"));
            return true;
        }
        if (type == null) {
            callback.onError(new MissingHardwareException("未配置安全认证等级"));
            return true;
        }
        return false;
    }
}
